package com.errol.batchprocessor.batchprocessor;

import com.errol.batchprocessor.batchprocessor.advisor.*;
import com.errol.batchprocessor.batchprocessor.payload.AbstractBatchPayload;
import com.errol.batchprocessor.batchprocessor.payload.AbstractPiecePayload;
import org.springframework.lang.NonNull;

import java.util.List;

/**
 * 抽象批处理扩展点容器
 *
 * @author zhang xiao bin
 * @version v1.0
 * @date 2021/02/09
 * @since v5.0.5
 **/
@SuppressWarnings("unchecked")
public abstract class AbstractBatchProcessorAdvisorContainer<IE extends AbstractImportBatch, AT extends Enum<?>, BE, SE, BP extends AbstractBatchPayload<IE, AT, BE, SE>, PP extends AbstractPiecePayload<SE, SP>, SP extends AbstractSinglePayload<SE>>
        extends AbstractAdvisorContainer<AbstractBatchPayload<IE, AT, BE, SE>>
        implements
        BatchAddAdvisor<IE, AT, BE, SE, BP, PP, SP>, BatchStartAdvisor<IE, AT, BE, SE, BP, PP, SP>, BatchFinishAdvisor<IE, AT, BE, SE, BP, PP, SP>, BatchPostAdvisor<IE, AT, BE, SE, BP, PP, SP>,
        PieceStartAdvisor<IE, AT, BE, SE, BP, PP, SP>, PieceFinishAdvisor<IE, AT, BE, SE, BP, PP, SP>, PiecePostAdvisor<IE, AT, BE, SE, BP, PP, SP>,
        SingleStartAdvisor<IE, AT, BE, SE, BP, PP, SP>, SingleProcessAdvisor<IE, AT, BE, SE, BP, PP, SP>, SingleFinishAdvisor<IE, AT, BE, SE, BP, PP, SP>, SinglePostAdvisor<IE, AT, BE, SE, BP, PP, SP> {

    public AbstractBatchProcessorAdvisorContainer(LogService logService) {
        super(logService);
    }

    /**
     * 批处理模型的扩展点容器，查找 AbstractAdvisor 的直接子类，包括：
     * 1、默认加载批处理通用扩展点；2、自定义扩展点（需要继承 AbstractAdvisor）
     *
     * @param advisor 执行 setApplicationContext 方法的类实现的接口
     * @return AbstractAdvisor 的直接子类
     */
    @Override
    protected final Class<? extends AbstractAdvisor<AbstractBatchPayload<IE, AT, BE, SE>>> findAdvisorClass(Class<?> advisor) {
        Class<?> child = advisor;
        while (child.getInterfaces().length == 1) {
            if (AbstractAdvisor.class.equals(child.getInterfaces()[0])) {
                return (Class<? extends AbstractAdvisor<AbstractBatchPayload<IE, AT, BE, SE>>>) child;
            } else {
                child = child.getInterfaces()[0];
            }
        }
        return null;
    }

    @Override
    public final List<PP> doSplitOnBatchAdded(@NonNull BP batchPayload, @NonNull ThreadPoolStatusBO threadPoolStatus) {
        return doCallList(batchPayload, BatchAddAdvisor.class, a -> a.doSplitOnBatchAdded(batchPayload, threadPoolStatus));
    }

    @Override
    public final boolean doPrepareOnBatchStart(@NonNull BP batchPayload) {
        return doCallBool(batchPayload, BatchStartAdvisor.class, a -> a.doPrepareOnBatchStart(batchPayload));
    }

    @Override
    public final boolean doSettleOnBatchProcessed(@NonNull BP batchPayload) {
        return doCallBool(batchPayload, BatchFinishAdvisor.class, a -> a.doSettleOnBatchProcessed(batchPayload));
    }

    @Override
    public final boolean doRecoverOnBatchException(@NonNull BP batchPayload, String errorMessage) {
        return doCallBool(batchPayload, BatchFinishAdvisor.class, a -> a.doRecoverOnBatchException(batchPayload, errorMessage));
    }

    @Override
    public final boolean doCleanUpOnBatchPost(@NonNull BP batchPayload) {
        boolean b = doCallBool(batchPayload, BatchPostAdvisor.class, a -> a.doCleanUpOnBatchPost(batchPayload));
        removeAdvisorCacheForMission(batchPayload);
        return b;
    }

    @Override
    public final boolean doPrepareOnPieceStart(@NonNull PP piecePayload, @NonNull BP batchPayload) {
        return doCallBool(batchPayload, PieceStartAdvisor.class, a -> a.doPrepareOnPieceStart(piecePayload, batchPayload));
    }

    @Override
    public final boolean doSettleOnPieceProcessed(@NonNull PP piecePayload, @NonNull BP batchPayload) {
        return doCallBool(batchPayload, PieceFinishAdvisor.class, a -> a.doSettleOnPieceProcessed(piecePayload, batchPayload));
    }

    @Override
    public final boolean doRecoverOnPieceException(@NonNull PP piecePayload, @NonNull BP batchPayload, String errorMessage) {
        return doCallBool(batchPayload, PieceFinishAdvisor.class, a -> a.doRecoverOnPieceException(piecePayload, batchPayload, errorMessage));
    }

    @Override
    public final boolean doCleanUpOnPiecePost(@NonNull PP piecePayload, @NonNull BP batchPayload) {
        return doCallBool(batchPayload, PiecePostAdvisor.class, a -> a.doCleanUpOnPiecePost(piecePayload, batchPayload));
    }

    @Override
    public final boolean doPrepareOnSingleStart(@NonNull SP singlePayload, @NonNull PP piecePayload, @NonNull BP batchPayload) {
        return doCallBool(batchPayload, SingleStartAdvisor.class, a -> a.doPrepareOnSingleStart(singlePayload, piecePayload, batchPayload));
    }

    @Override
    public final boolean doValidateBeforeSingleProcess(@NonNull SP singlePayload, @NonNull PP piecePayload, @NonNull BP batchPayload) {
        return doCallBool(batchPayload, SingleProcessAdvisor.class, a -> a.doValidateBeforeSingleProcess(singlePayload, piecePayload, batchPayload));
    }

    @Override
    public final boolean doExecuteOnSingleProcess(@NonNull SP singlePayload, @NonNull PP piecePayload, @NonNull BP batchPayload) {
        return doCallBool(batchPayload, SingleProcessAdvisor.class, a -> a.doExecuteOnSingleProcess(singlePayload, piecePayload, batchPayload));
    }

    @Override
    public final boolean doSettleOnSingleProcessed(@NonNull SP singlePayload, @NonNull PP piecePayload, @NonNull BP batchPayload) {
        return doCallBool(batchPayload, SingleFinishAdvisor.class, a -> a.doSettleOnSingleProcessed(singlePayload, piecePayload, batchPayload));
    }

    @Override
    public final boolean doRecoverOnSingleException(@NonNull SP singlePayload, @NonNull PP piecePayload, @NonNull BP batchPayload, String errorMessage) {
        return doCallBool(batchPayload, SingleFinishAdvisor.class, a -> a.doRecoverOnSingleException(singlePayload, piecePayload, batchPayload, errorMessage));
    }

    @Override
    public boolean doCleanUpOnSinglePost(@NonNull SP singlePayload, @NonNull PP piecePayload, @NonNull BP batchPayload) {
        return doCallBool(batchPayload, SinglePostAdvisor.class, a -> a.doCleanUpOnSinglePost(singlePayload, piecePayload, batchPayload));
    }

    protected <A extends AbstractAdvisor<AbstractBatchPayload<IE, AT, BE, SE>>> List<PP> doCallList(BP batchPayload, Class<A> tClass, AbstractAdvisorCaller<A> advisorCaller) {
        A a;
        for (AbstractAdvisor<AbstractBatchPayload<IE, AT, BE, SE>> employeeAdvisor : getAdvisorMap(batchPayload).get(tClass)) {
            a = (A) employeeAdvisor;
            List<PP> pps = (List<PP>) advisorCaller.doCall(a);
            if (pps != null) {
                return pps;
            }
        }
        return null;
    }

}
